package com.mega.ycy.mixin;

import com.mega.ycy.Decide;
import com.mega.ycy.Saver;
import com.mega.ycy.Yiciyuan_EVENT;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.MusicTicker;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.GuiGameOver;
import net.minecraft.client.gui.GuiIngame;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSleepMP;
import net.minecraft.client.main.GameConfiguration;
import net.minecraft.client.multiplayer.PlayerControllerMP;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.RenderItem;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.tutorial.Tutorial;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.Entity;
import net.minecraft.network.NetworkManager;
import net.minecraft.profiler.Profiler;
import net.minecraft.util.ReportedException;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.EnumDifficulty;
import net.minecraftforge.client.GuiIngameForge;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Objects;

@Mixin(Minecraft.class)
public abstract class MinecraftMixin {
    @Shadow public EntityPlayerSP player;

    @Shadow public WorldClient world;

    @Shadow public int rightClickDelayTimer;

    @Shadow public Profiler mcProfiler;

    @Shadow public GuiIngame ingameGUI;

    @Shadow public EntityRenderer entityRenderer;

    @Shadow public RayTraceResult objectMouseOver;

    @Shadow public Tutorial tutorial;

    @Shadow public boolean isGamePaused;

    @Shadow public PlayerControllerMP playerController;

    @Shadow public TextureManager renderEngine;

    @Shadow @Nullable public GuiScreen currentScreen;

    @Shadow public abstract void displayGuiScreen(@Nullable GuiScreen guiScreenIn);

    @Shadow public int leftClickCounter;

    @Shadow public int joinPlayerCounter;

    @Shadow public abstract void runTickKeyboard() throws IOException;

    @Shadow public abstract void runTickMouse() throws IOException;

    @Shadow public MusicTicker mcMusicTicker;

    @Shadow public SoundHandler mcSoundHandler;

    @Shadow public RenderGlobal renderGlobal;

    @Shadow public ParticleManager effectRenderer;

    @Shadow @Nullable public NetworkManager myNetworkManager;

    @Shadow public long systemTime;

    @Shadow
    public static long getSystemTime() {
        return 0;
    }

    @Shadow public RenderManager renderManager;

    @Shadow public static Logger LOGGER;

    @Shadow public abstract void rightClickMouse();

    @Shadow public abstract void processKeyBinds();

    @Shadow public IReloadableResourceManager mcResourceManager;

    @Shadow public abstract TextureManager getTextureManager();

    @Shadow public abstract RenderItem getRenderItem();

    /**
     * @author MegaDarkness
     * @reason To prevent other moders from modifying variables
     */
    @Inject(method = "<init>", at = @At(value = "RETURN"))
    private void init(GameConfiguration gameConfig, CallbackInfo ci) {
        TWEAKER.CLIENT_PROFILER = new Saver(mcProfiler);
        TWEAKER.ENTITY_RENDERER = new Saver(entityRenderer);
        TWEAKER.RENDER_GLOBAL = new Saver(renderGlobal);
        TWEAKER.RENDER_MANAGER = new Saver(renderManager);
        TWEAKER.INGAME_GUI = new Saver(new GuiIngameForge((Minecraft) (Object)this));
    }

    /**
     * @author run
     */
    @Inject(method = "func_99999_d", at = @At("HEAD"))
    public void run1(CallbackInfo ci) {
        TWEAKER.reloadFields();
        Yiciyuan_EVENT.protectClientScreen();
    }

    /**
     * @author run
     */
    @Inject(method = "func_99999_d", at = @At("FIELD"))
    public void run_1(CallbackInfo ci) {
        TWEAKER.reloadFields();
        Yiciyuan_EVENT.protectClientScreen();
    }

    /**
     * @author run
     */
    @Inject(method = "func_99999_d", at = @At("RETURN"))
    public void run2(CallbackInfo ci) {
        TWEAKER.reloadFields();
        Yiciyuan_EVENT.protectClientScreen();
    }


    /**
     * @author run
     */
    @Inject(method = "func_99999_d", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;func_71411_J()V"))
    public void run_INVOKE(CallbackInfo ci) {
        if (TWEAKER.isTimeStop) {
            for (int j = 0; j < Math.min(10, TWEAKER.NORMAL.elapsedTicks); ++j) {
                if (Yiciyuan_EVENT.isSafe(player)) {
                    processKeyBinds();
                    world.updateEntity(player);
                    if (Yiciyuan_EVENT.getServerPlayer(player) != null)
                        Yiciyuan_EVENT.getServerPlayer(player).world.updateEntity(Yiciyuan_EVENT.getServerPlayer(player));
                    try {
                        runTickKeyboard();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    rightClickMouse();
                }
            }
        }
    }


    /**
     * @author displayGuiScreen
     */
    @Inject(method = "func_147108_a", at = @At(value = "HEAD"), cancellable = true)
    public void displayGuiScreen(GuiScreen guiScreenIn, CallbackInfo ci) {
        if (Yiciyuan_EVENT.isSafe(player)) {
            if (Yiciyuan_EVENT.isIllegal(guiScreenIn))
                ci.cancel();
        }
    }

    private boolean isTimestop() {
        return TWEAKER.isTimeStop;
    }

    @Overwrite
    public void runTick() throws IOException
    {
        if (this.rightClickDelayTimer > 0)
        {
            --this.rightClickDelayTimer;
        }

        if (!isTimestop())
            net.minecraftforge.fml.common.FMLCommonHandler.instance().onPreClientTick();

        this.mcProfiler.startSection("gui");

        if (!isTimestop())
        {
            this.ingameGUI.updateTick();
        }

        this.mcProfiler.endSection();
        this.entityRenderer.getMouseOver(1.0F);
        this.tutorial.onMouseHover(this.world, this.objectMouseOver);
        this.mcProfiler.startSection("gameMode");

        if (!this.isGamePaused && this.world != null)
        {
            this.playerController.updateController();
        }

        this.mcProfiler.endStartSection("textures");

        if (!isTimestop())
        {
            this.renderEngine.tick();
        }

        if (this.currentScreen == null && this.player != null)
        {
            if (this.player.getHealth() <= 0.0F && !(this.currentScreen instanceof GuiGameOver))
            {
                this.displayGuiScreen(null);
            }
            else if (this.player.isPlayerSleeping() && this.world != null)
            {
                this.displayGuiScreen(new GuiSleepMP());
            }
        }
        else if (this.currentScreen != null && this.currentScreen instanceof GuiSleepMP && !this.player.isPlayerSleeping())
        {
            this.displayGuiScreen(null);
        }

        if (this.currentScreen != null)
        {
            this.leftClickCounter = 10000;
        }

        if (this.currentScreen != null)
        {
            try
            {
                this.currentScreen.handleInput();
            }
            catch (Throwable throwable1)
            {
                CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Updating screen events");
                CrashReportCategory crashreportcategory = crashreport.makeCategory("Affected screen");
                crashreportcategory.addDetail("Screen name", () -> {
                    if (Minecraft.getMinecraft().currentScreen != null) {
                        return Minecraft.getMinecraft().currentScreen.getClass().getCanonicalName();
                    }
                    return null;
                });
                throw new ReportedException(crashreport);
            }

            if (this.currentScreen != null)
            {
                try
                {
                    this.currentScreen.updateScreen();
                }
                catch (Throwable throwable)
                {
                    CrashReport crashreport1 = CrashReport.makeCrashReport(throwable, "Ticking screen");
                    CrashReportCategory crashreportcategory1 = crashreport1.makeCategory("Affected screen");
                    crashreportcategory1.addDetail("Screen name", () -> {
                        if (Minecraft.getMinecraft().currentScreen != null) {
                            return Minecraft.getMinecraft().currentScreen.getClass().getCanonicalName();
                        }
                        return null;
                    });
                    throw new ReportedException(crashreport1);
                }
            }
        }

        if (this.currentScreen == null || this.currentScreen.allowUserInput)
        {
            this.mcProfiler.endStartSection("mouse");
            this.runTickMouse();

            if (this.leftClickCounter > 0)
            {
                --this.leftClickCounter;
            }

            this.mcProfiler.endStartSection("keyboard");
            this.runTickKeyboard();
        }

        if (this.world != null)
        {
            if (this.player != null)
            {
                ++this.joinPlayerCounter;

                if (this.joinPlayerCounter == 30)
                {
                    this.joinPlayerCounter = 0;
                    this.world.joinEntityInSurroundings(this.player);
                }
            }

            this.mcProfiler.endStartSection("gameRenderer");

            if (!isTimestop())
            {
                this.entityRenderer.updateRenderer();
            }

            this.mcProfiler.endStartSection("levelRenderer");

            if (!isTimestop())
            {
                this.renderGlobal.updateClouds();
            }

            this.mcProfiler.endStartSection("level");

            if (!isTimestop())
            {
                if (this.world.getLastLightningBolt() > 0)
                {
                    this.world.setLastLightningBolt(this.world.getLastLightningBolt() - 1);
                }

                this.world.updateEntities();
            } else {
                for (int i = 0; i < world.weatherEffects.size(); ++i) {
                    Entity entity = world.weatherEffects.get(i);

                    try {
                        if (entity.updateBlocked) continue;
                        if (!Yiciyuan_EVENT.isSafe(entity)) continue;
                        ++entity.ticksExisted;
                        entity.onUpdate();
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }
                if (player != null)
                    for (int i = 0; i < player.world.playerEntities.size(); ++i) {
                        Entity entity = player.world.playerEntities.get(i);

                        try {
                            if (!Yiciyuan_EVENT.isSafe(entity)) continue;
                            player.world.updateEntity(entity);
                        } catch (Throwable throwable) {
                            throwable.printStackTrace();
                        }
                    }
            }
            if (player != null && Decide.isWitherWorld(player.world)) {
                for (int i = 0; i < world.weatherEffects.size(); ++i) {
                    Entity entity = world.weatherEffects.get(i);

                    try {
                        if (entity.updateBlocked) continue;
                        if (!Yiciyuan_EVENT.isSafe(entity)) continue;
                        ++entity.ticksExisted;
                        entity.onUpdate();
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }
                if (player != null)
                    for (int i = 0; i < player.world.playerEntities.size(); ++i) {
                        Entity entity = player.world.playerEntities.get(i);

                        try {
                            if (!Yiciyuan_EVENT.isSafe(entity)) continue;
                            player.world.updateEntity(entity);
                        } catch (Throwable throwable) {
                            throwable.printStackTrace();
                        }
                    }
            }
        }
        else if (this.entityRenderer.isShaderActive())
        {
            this.entityRenderer.stopUseShader();
        }

        if (!isTimestop())
        {
            this.mcMusicTicker.update();
            this.mcSoundHandler.update();
        }

        if (this.world != null)
        {
            if (!isTimestop())
            {
                this.world.setAllowedSpawnTypes(this.world.getDifficulty() != EnumDifficulty.PEACEFUL, true);
                this.tutorial.update();

                try
                {
                    this.world.tick();
                }
                catch (Throwable throwable2)
                {
                    CrashReport crashreport2 = CrashReport.makeCrashReport(throwable2, "Exception in world tick");

                    if (this.world == null)
                    {
                        CrashReportCategory crashreportcategory2 = crashreport2.makeCategory("Affected level");
                        crashreportcategory2.addCrashSection("Problem", "Level is null!");
                    }
                    else
                    {
                        this.world.addWorldInfoToCrashReport(crashreport2);
                    }

                    throw new ReportedException(crashreport2);
                }
            }

            this.mcProfiler.endStartSection("animateTick");

            if (!isTimestop() && this.world != null )
            {
                this.world.doVoidFogParticles(MathHelper.floor(this.player.posX), MathHelper.floor(this.player.posY), MathHelper.floor(this.player.posZ));
            }

            this.mcProfiler.endStartSection("particles");

            if (!isTimestop())
            {
                this.effectRenderer.updateEffects();
            }
        }
        else if (this.myNetworkManager != null)
        {
            this.mcProfiler.endStartSection("pendingConnection");
            this.myNetworkManager.processReceivedPackets();
        }

        this.mcProfiler.endSection();
        if (!isTimestop())
            net.minecraftforge.fml.common.FMLCommonHandler.instance().onPostClientTick();
        this.systemTime = getSystemTime();
    }
}
